"""
数据集解压后文件结构校验模块。
EN: Dataset unpacking file structure verification module.

该模块提供了一系列函数，用于在数据集解压后，根据预定义的配置检查其文件和目录结构是否符合要求。
EN: This module provides a series of functions to check if the file and directory structure of a dataset
after unpacking conforms to the requirements, based on predefined configurations.
"""

# 导入日志模块，用于记录程序运行时的信息
# EN: Import the logging module, used for recording information during program execution
import logging
# 导入 os.path 模块，用于处理文件路径
# EN: Import the os.path module, used for handling file paths
import os.path

# 从 myapp.configs.dataset 模块导入 DATASET_CHECKING_CONFIGS，该配置定义了不同数据集类型的校验规则
# EN: Import DATASET_CHECKING_CONFIGS from myapp.configs.dataset module, which defines
# EN: validation rules for different dataset types
from myapp.configs.dataset import DATASET_CHECKING_CONFIGS

# 默认的数据集校验配置，当 DATASET_CHECKING_CONFIGS 未配置时使用
# EN: Default dataset checking configurations, used when DATASET_CHECKING_CONFIGS is not configured
default_dataset_checking_configs = {
    'Image Captioning': {
        'demo': 'demo_data/image_captioning.zip',
        'template_code': 'Image Caption',
        'must_directories': [{'path': 'img/', 'ext': 'jpg|jpeg', 'least': 1}],
        'must_files': [
            {'path': 'txt/json_coco_train_zh_token_full.json', 'fmt': 'json'},
            {'path': 'ids_to_tokens_zh.json', 'fmt': 'json'},
        ],
    }
}


def load_dataset_checking_configs():
    """
    加载数据集校验配置。
    EN: Loads the dataset checking configurations.

    首先尝试从 `DATASET_CHECKING_CONFIGS` 中加载配置。
    如果 `DATASET_CHECKING_CONFIGS` 为空，则使用 `default_dataset_checking_configs`。
    EN: First, it attempts to load configurations from `DATASET_CHECKING_CONFIGS`.
    EN: If `DATASET_CHECKING_CONFIGS` is empty, it falls back to `default_dataset_checking_configs`.

    Returns:
        dict: 数据集校验配置字典。
        EN: dict: A dictionary of dataset checking configurations.
    """
    # cfgs = get_config("checking_config", "DATASET")
    # 从 DATASET_CHECKING_CONFIGS 中获取配置，并转换为字典
    # EN: Get configurations from DATASET_CHECKING_CONFIGS and convert to a dictionary
    cfgs = DATASET_CHECKING_CONFIGS.to_dict()
    # 如果配置为空
    # EN: If configurations are empty
    if cfgs is None:
        # 记录日志，表示使用默认配置
        # EN: Log a message indicating that default configurations are being used
        logging.info('nacos未配置checking_config，改读默认配置')
        # 使用默认配置
        # EN: Use default configurations
        cfgs = default_dataset_checking_configs
    # 返回加载的配置
    # EN: Return the loaded configurations
    return cfgs


def check_dataset_path(dataset_path: str, template_code: str) -> str or None:
    """
    根据模板代码检查数据集路径是否符合要求。
    EN: Checks if the dataset path conforms to requirements based on the template code.

    Args:
        dataset_path (str): 数据集在文件系统中的绝对路径。
        EN: dataset_path (str): The absolute path of the dataset in the file system.
        template_code (str): 用于指定数据集类型的模板代码，例如 'Image Captioning'。
        EN: template_code (str): The template code specifying the dataset type, e.g., 'Image Captioning'.

    Returns:
        str or None: 如果存在校验错误，返回错误信息字符串；否则返回 None。
        EN: str or None: Returns an error message string if there is a validation error; otherwise, returns None.
    """
    # 加载数据集校验配置
    # EN: Load dataset checking configurations
    cfgs = load_dataset_checking_configs()
    # 根据模板代码获取对应的数据集配置
    # EN: Get the dataset configuration corresponding to the template code
    cfg = cfgs.get(template_code, None)
    # 如果没有找到对应的配置，则直接返回 None，表示无需校验或校验通过
    # EN: If no corresponding configuration is found, return None directly, indicating no validation is needed or it passes
    if cfg is None:
        return None

    # 获取必须存在的目录配置
    # EN: Get configurations for required directories
    directories = cfg.get('must_directories', None)
    # 如果存在目录配置
    # EN: If directory configurations exist
    if directories is not None:
        # 检查目录是否符合要求
        # EN: Check if directories conform to requirements
        res = check_directories(dataset_path, directories)
        # 如果目录检查返回错误，则直接返回错误信息
        # EN: If directory checking returns an error, return the error message directly
        if res is not None:
            return res

    # 获取必须存在的文件配置
    # EN: Get configurations for required files
    files = cfg.get('must_files', None)
    # 如果存在文件配置
    # EN: If file configurations exist
    if files is not None:
        # 检查文件是否符合要求
        # EN: Check if files conform to requirements
        res = check_files(dataset_path, files)
        # 返回文件检查结果
        # EN: Return the file checking result
        return res


def check_directories(dataset_path: str, directories: list) -> str or None:
    """
    检查数据集路径下的目录结构是否符合要求。
    EN: Checks if the directory structure under the dataset path conforms to requirements.

    Args:
        dataset_path (str): 数据集在文件系统中的绝对路径。
        EN: dataset_path (str): The absolute path of the dataset in the file system.
        directories (list): 包含目录校验规则的列表，每个元素是一个字典，
                            包含 'path' (目录相对路径), 'ext' (允许的文件扩展名，用'|'分隔),
                            和 'least' (该目录下至少需要的文件数量)。
        EN: directories (list): A list containing directory validation rules, where each element is a dictionary
                                containing 'path' (relative path of the directory), 'ext' (allowed file extensions, separated by '|'),
                                and 'least' (minimum number of files required in that directory).

    Returns:
        str or None: 如果存在目录校验错误，返回错误信息字符串；否则返回 None。
        EN: str or None: Returns an error message string if there is a directory validation error; otherwise, returns None.
    """
    # 遍历所有需要检查的目录配置
    # EN: Iterate through all directory configurations that need to be checked
    for d in directories:
        # 构建完整的目录路径
        # EN: Construct the full directory path
        full_dir_path = os.path.join(dataset_path, d.get('path'))
        # 检查目录是否存在
        # EN: Check if the directory exists
        if not os.path.exists(full_dir_path):
            # 如果目录不存在，返回错误信息
            # EN: If the directory does not exist, return an error message
            return '目录{}不存在'.format(d.get('path'))
        # 获取允许的文件扩展名
        # EN: Get allowed file extensions
        exts = d.get('ext')
        # 如果定义了文件扩展名限制
        # EN: If file extension restrictions are defined
        if exts is not None:
            # 将扩展名字符串分割成列表
            # EN: Split the extension string into a list
            image_extensions = exts.split('|')
            # 初始化合法文件计数器
            # EN: Initialize a counter for valid files
            image_count = 0
            # 遍历目录及其子目录中的文件
            # EN: Iterate through files in the directory and its subdirectories
            for _, _, files in os.walk(full_dir_path):
                for file in files:
                    # 获取文件扩展名并转换为小写，去除开头的点
                    # EN: Get the file extension, convert to lowercase, and remove the leading dot
                    file_ext = os.path.splitext(file)[1].lower().lstrip('.')
                    # 如果文件扩展名在允许的列表中
                    # EN: If the file extension is in the allowed list
                    if file_ext in image_extensions:
                        # 增加合法文件计数
                        # EN: Increment the valid file count
                        image_count += 1
            # 检查合法文件数量是否达到最小值
            # EN: Check if the number of valid files meets the minimum requirement
            if image_count < d.get('least', 0):
                # 如果不满足，返回错误信息
                # EN: If not satisfied, return an error message
                return '目录{}下的合法文件少于{}个'.format(d.get('path'), d.get('least'))
    # 所有目录检查通过，返回 None
    # EN: All directory checks passed, return None
    return None


def check_files(dataset_path: str, files: list) -> str or None:
    """
    检查数据集路径下的文件是否存在。
    EN: Checks if files exist under the dataset path.

    Args:
        dataset_path (str): 数据集在文件系统中的绝对路径。
        EN: dataset_path (str): The absolute path of the dataset in the file system.
        files (list): 包含文件校验规则的列表，每个元素是一个字典，包含 'path' (文件相对路径)。
        EN: files (list): A list containing file validation rules, where each element is a dictionary
                          containing 'path' (relative path of the file).

    Returns:
        str or None: 如果存在文件校验错误，返回错误信息字符串；否则返回 None。
        EN: str or None: Returns an error message string if there is a file validation error; otherwise, returns None.
    """
    # 遍历所有需要检查的文件配置
    # EN: Iterate through all file configurations that need to be checked
    for f in files:
        # 构建完整的文件路径
        # EN: Construct the full file path
        full_file_path = os.path.join(dataset_path, f.get('path'))
        # 检查文件是否存在
        # EN: Check if the file exists
        if not os.path.exists(full_file_path):
            # 如果文件不存在，返回错误信息
            # EN: If the file does not exist, return an error message
            return '文件{}不存在'.format(f.get('path'))
    # 所有文件检查通过，返回 None
    # EN: All file checks passed, return None
    return None


# 以下是被注释掉的旧版校验函数，目前未使用。
# EN: The following are commented-out older validation functions, currently not in use.

# if __name__ == "__main__":
#     res = check_dataset_path("./qianbo_data", "Image Captioning")
#     print(res)


# def check_dataset_path(path, template_code):
#     if template_code == "Image Captioning":
#         return check_image_captioning(path)
#     if template_code == "Audio Classification":
#         return check_audio_classification(path)
#     if template_code == "Object Detection with Polygons":
#         return check_object_detection_with_polygons(path)
#     return None


# dataset_id 12
# train_data/qianbo_data/
def check_image_captioning(path: str) -> str or None:
    """
    检查图像标注数据集的路径是否符合要求 (旧版函数，目前未使用)。
    EN: Checks if the path for an image captioning dataset conforms to requirements (older function, currently not in use).

    Args:
        path (str): 数据集在文件系统中的绝对路径。
        EN: path (str): The absolute path of the dataset in the file system.

    Returns:
        str or None: 如果存在校验错误，返回错误信息字符串；否则返回 None。
        EN: str or None: Returns an error message string if there is a validation error; otherwise, returns None.
    """
    # 构建 token JSON 文件的完整路径
    # EN: Construct the full path for the token JSON file
    token_json = os.path.join(path, 'ids_to_tokens_zh.json')

    # 检查 token JSON 文件是否存在
    # EN: Check if the token JSON file exists
    if not os.path.exists(token_json):
        return '缺少文件ids_to_tokens_zh.json'

    # 检查 img/ 目录是否存在
    # EN: Check if the img/ directory exists
    if not os.path.exists(os.path.join(path, 'img/')):
        return '目录img不存在'

    # 检查 label JSON 文件是否存在
    # EN: Check if the label JSON file exists
    if not os.path.exists(os.path.join(path, 'txt/json_coco_train_zh_token_full.json')):
        return '缺少文件txt/json_coco_train_zh_token_full.json'

    # 检查 meta JSON 文件是否存在
    # EN: Check if the meta JSON file exists
    if not os.path.exists(os.path.join(path, 'txt/meta.json')):
        return '缺少文件txt/meta.json'

    # 所有检查通过，返回 None
    # EN: All checks passed, return None
    return None


# dataset_id 22
# train_data/vit_detection_2.0/
def check_object_detection_with_polygons(path: str) -> str or None:
    """
    检查带有多边形标注的对象检测数据集的路径是否符合要求 (旧版函数，目前未使用)。
    EN: Checks if the path for an object detection dataset with polygon annotations conforms to requirements (older function, currently not in use).

    Args:
        path (str): 数据集在文件系统中的绝对路径。
        EN: path (str): The absolute path of the dataset in the file system.

    Returns:
        str or None: 如果存在校验错误，返回错误信息字符串；否则返回 None。
        EN: str or None: Returns an error message string if there is a validation error; otherwise, returns None.
    """
    # 检查 label_config.json 文件是否存在
    # EN: Check if the label_config.json file exists
    if not os.path.exists(os.path.join(path, 'label_config.json')):
        return '缺少文件label_config.json'

    # 检查 result.json 文件是否存在
    # EN: Check if the result.json file exists
    if not os.path.exists(os.path.join(path, 'result.json')):
        return '缺少result.json'

    # 检查 image/ 目录是否存在
    # EN: Check if the image/ directory exists
    if not os.path.exists(os.path.join(path, 'image/')):
        return '目录image不存在'

    # 所有检查通过，返回 None
    # EN: All checks passed, return None
    return None


# dataset_id 18
# train_data/transformer_classification_data_2.0/
def check_audio_classification(path: str) -> str or None:
    """
    检查音频分类数据集的路径是否符合要求 (旧版函数，目前未使用)。
    EN: Checks if the path for an audio classification dataset conforms to requirements (older function, currently not in use).

    Args:
        path (str): 数据集在文件系统中的绝对路径。
        EN: path (str): The absolute path of the dataset in the file system.

    Returns:
        str or None: 如果存在校验错误，返回错误信息字符串；否则返回 None。
        EN: str or None: Returns an error message string if there is a validation error; otherwise, returns None.
    """
    # 检查 result.json 文件是否存在
    # EN: Check if the result.json file exists
    if not os.path.exists(os.path.join(path, 'result.json')):
        return '缺少result.json'

    # 检查 audio/ 目录是否存在
    # EN: Check if the audio/ directory exists
    if not os.path.exists(os.path.join(path, 'audio/')):
        return '目录audio不存在'

    # 所有检查通过，返回 None
    # EN: All checks passed, return None
    return None
